home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-01-02 | 89.4 KB | 3,134 lines |
- ************************* INTRODUCTION *************************
-
- This document is a tutorial on the use of the header file <iostream.h>, as
- implemented by Borland International, Inc. in its product Turbo C++.
-
- There are 4 main sections:
-
- 1) Header file
- 2) Output
- 3) Input
- 4) Manipulators
- 5) File I/O
-
- These sections are actually just part of the complete set of notes on the C++
- language that I use in my programming classes here in Silicon Valley. Before
- you begin this tutorial, you must be familiar with C, know how to write simple
- classes, have some knowledge of member functions, can create instances of
- classes, etc.
-
- I have spent many hours putting this material together, and hope that it
- serves its intended purpose, which is to give CIS subscribers an understand-
- ing of <iostream.h> that none of the manuals or textbooks seem to provide.
- I am deeply indebted to John Dlugosz, with whom many of you are already
- familiar, for taking the time out of his busy schedule, to proof-read this
- tutorial. Without his help and guidance, the information contained herein
- would not be as accurate and complete as (I hope!) it is. Nevertheless, if
- you find an error, or have a suggestion as to how this tutorial can be
- improved, please let me know.
-
- Eric Nagler CIS 76357,1146
- P. O. Box 2483
- Santa Clara, California 95055-2483
-
-
- *********************** SECTION 1 -- HEADER FILE ***************************
-
- All program examples in this tutorial include a file called <header.h>. It is
- my own creation (so don't call Borland!), and is tailored to accommodate
- all of the program examples. I suggest that you copy and use this header file
- if you wish to execute the examples. But feel free to modify it according to
- your own needs.
-
- If the examples sometimes appear to be "scrunched" toward the left-hand side
- of the screen, it's because they are designed to be shown in 40-column mode,
- double height.
-
- ////////////////////////////////////
- // My private header file for C++ programs
- ////////////////////////////////////
-
- // There's also a #define ZORTECH in my Zortech header file so that I can
- // write different statements to print addresses.
-
- #define BORLAND
-
- // OLD is defined in the cc.bat file which I use to compile Turbo C++
- // using the old (version 1.2) style I/O. Therefore, use <stream.h>
-
- #ifdef OLD
- #include <stream.h>
-
- // Otherwise, it must be version 2.0, so use <fstream.h>, which also
- // includes <iostream.h>
-
- #else
- #include <fstream.h>
-
- // Any manipulators taking a parameter will also be accommodated
-
- #include <iomanip.h>
- #endif
-
- // I also need console I/O for the PAUSE() function
-
- #include <conio.h>
-
- // Include <stdio.h> for C style I/O
-
- #include <stdio.h>
-
- // Include these because they're used so frequently, and really don't add
- // that much to the compilation time
-
- #include <string.h>
- #include <ctype.h>
- #include <stdlib.h>
- #include <time.h>
-
- ///////////////////////////////////
-
- // My private defines
-
- #define FALSE 0
- #define TRUE !FALSE
- #define AND &&
- #define OR ||
- #define NO 0
- #define YES !NO
- #define EQUALS ==
- #define IS_EQUAL_TO ==
- #define NOT !
- #define IS_NOT_EQUAL_TO !=
- #define NOT_EQUAL_TO !=
- //
- #define BLANK ' '
- #define SPACE ' '
- #define ASTERISK '*'
- #define DECIMAL '.'
- #define NEW_LINE '\n'
- #define NUL '\0'
- #define TAB '\t'
- #define BACKSPACE '\b'
- #define BEEP '\a'
- #define FORMFEED '\f'
- #define RETURN '\r'
-
- ///////////////////////////////////
-
- // My private PAUSE function
-
- void PAUSE()
- {
- cout << "Press any key to "
- << "continue...\n" ;
- getch() ;
- }
-
- // My private flush-the-input-buffer manipulator until either EOF or
- // new-line is found.
-
- istream& FLUSH(istream& strm)
- {
- strm.clear() ;
- char ch ;
- while (!strm.get(ch).eof() AND ch != NEW_LINE)
- ; // Empty body
- return strm ;
- }
-
- ////////////////////////////////////
- // This function prints the various I/O flags of an input stream.
- // Defaults to 'cin'
-
- void IOFLAGS(istream& stream=cin)
- {
- cout << "eof state is " << (stream.eof()
- ? "ON" : "OFF") << endl ;
- cout << "good state is " << (stream.good()
- ? "ON" : "OFF") << endl ;
- cout << "fail state is " << (stream.fail()
- ? "ON" : "OFF") << endl ;
- cout << "bad state is " << (stream.bad()
- ? "ON" : "OFF") << endl ;
- cout << "if (!instance) returns "<< (!stream ? "TRUE" : "FALSE") << endl ;
- cout << "if (instance) returns " << (stream ? "TRUE" : "FALSE") << endl ;
- }
-
- /////////////////////////////////////
- // This function prints the format flags of an output stream.
- // Defaults to 'cout'
-
- void FORMATFLAGS(ostream& stream=cout)
- {
- static char* message[] =
- {
- "skipws" ,
- "left" ,
- "right" ,
- "internal" ,
- "dec" ,
- "oct" ,
- "hex" ,
- "showbase" ,
- "showpoint" ,
- "uppercase" ,
- "showpos" ,
- "scientific" ,
- "fixed" ,
- "unitbuf" ,
- "stdio"
- } ;
-
- cout << "FORMAT FLAGS\n" ;
- long f = stream.flags() ;
- for (int i = 0 ; i < 16 ; ++i)
- {
- if (f & 0x0001)
- cout << message[i] << endl ;
- f >>= 1 ;
- }
- }
-
- ///////////////////////////////////
-
- // Define a global instance of the
- // class 'ofstream' called POUT that
- // is tied to the printer
-
- ofstream POUT("prn") ;
-
- /////////////////////////////////////
-
- // A manipulator that directs to the printer all subsequent output for
- // the one statement in which it is found
-
- ostream& PRINTER(ostream&)
- {
- return POUT ;
- }
-
- // A manipulator that directs to the screen all subsequent output for
- // the one statement in which it is found
-
- ostream& SCREEN(ostream&)
- {
- return cout ;
- }
-
- // A manipluator to set left justification
-
- ostream& LEFT(ostream& str)
- {
- str.setf(ios::left , ios::adjustfield) ;
- return str ;
- }
-
- // A manipluator to set right justification
-
- ostream& RIGHT(ostream& str)
- {
- str.setf(ios::right , ios::adjustfield) ;
- return str ;
- }
-
- // A manipluator to set internal justification
-
- ostream& INTERNAL(ostream& str)
- {
- str.setf(ios::internal , ios::adjustfield) ;
- return str ;
- }
-
- // A manipulator to show the decimal point
-
- ostream& SHOWPOINT(ostream& str)
- {
- str.setf(ios::showpoint) ;
- return str ;
- }
-
- // A manipulator to show the base on hex and octal output
-
- ostream& SHOWBASE(ostream& str)
- {
- str.setf(ios::showbase) ;
- return str ;
- }
-
- // A manipulator to suppress the showing of the base
-
- ostream& NOSHOWBASE(ostream& str)
- {
- str.unsetf(ios::showbase) ;
- return str ;
- }
-
- // A manipulator to show fixed point output
-
- ostream& FIXED(ostream& str)
- {
- str.setf(ios::fixed , ios::floatfield) ;
- return str ;
- }
-
- // A manipulator to show scientific point output
-
- ostream& SCIENTIFIC(ostream& str)
- {
- str.setf(ios::scientific , ios::floatfield) ;
- return str ;
- }
-
- // A manipulator to show uppercase output on hex and scientific
- // numbers
-
- ostream& UPPERCASE(ostream& str)
- {
- str.setf(ios::uppercase) ;
- return str ;
- }
-
- // A manipulator to show lowercase output on hex and scientific
- // numbers
-
- ostream& LOWERCASE(ostream& str)
- {
- str.unsetf(ios::uppercase) ;
- return str ;
- }
-
- // A manipulator to show a '+' on positive numbers
-
- ostream& SHOWPOS(ostream& str)
- {
- str.setf(ios::showpos) ;
- return str ;
- }
-
- // A manipulator to negate showing a '+' on positive numbers
-
- ostream& NOSHOWPOS(ostream& str)
- {
- str.unsetf(ios::showpos) ;
- return str ;
- }
-
- *********************** SECTION 2 -- OUTPUT *********************************
-
- Introduction
-
- Whenever data gets sent to a text output device, it takes on the form of a
- stream of characters. For example, the floating point number 1.234 is not
- stored internally as 5 characters ('1' , '.' , '2' , '3' , '4'), but rather
- in a special format designed to accommodate floating point values. In character
- format, this value is meaningless. Yet, if you were to print this number,
- you certainly would want to have the aforementioned 5 characters appear
- on your output device.
-
- The C++ input/output mechanism provided with AT&T release 2.0
- provides a series of classes that have been created to handle the problem of
- sending and receiving data. This mechanism consists of many classes, some
- derived from others, and some contained within others. At the lowest level,
- the most fundamental operation is to manipulate the characters within some
- buffer, also known as a stream. This is done by the class "streambuf".
-
- Because input and output must be formatted to be legible, another class
- called "ios" contains functions and data to handle this task. When an instance
- of the class "ios" comes into existence, it receives a pointer to some
- "streambuf" area. To send the actual formatted output to the user, another
- class called "ostream" (which is derived from "ios") is used.
-
- Classes, like built-in scalar types, don't occupy any memory. What is
- needed, then, is an instance, or object, of that class type. In the hierarchy
- of derivation, the last class, ostream , is the one that used to create the
- instance, as follows:
-
- ostream cout ;
-
- Since the instance "cout" is an instance of a class that has been derived from
- parent classes, by definition it has inherited all of the data members of its
- parent classes, and has all of the functionality of those classes. Also, since
- this instance is defined at global scope, your program has unlimited access
- to it at all times. Output operations are initiated using the instance "cout".
-
- In addition to data members, classes can contain functions to operate upon
- these members. In the class ostream , the function you will use the most
- often is called the insertion operator, and its name is "operator<<". The
- argument to this function is the data item that you wish to output. The name
- 'insertion' comes from the fact that you are 'inserting' items into an
- output buffer.
-
- In a manner similar to that of accessing a data member of a structure,
- function members of a class are also accessed using the direct member
- operator, also known as the dot operator (.). This is done by first writing
- the instance, followed by the dot operator, and then the function name with
- any arguments enclosed within parentheses. For example, to output a simple
- message, you would code:
-
- cout.operator<<("THE ANSWER IS ") ;
-
- where "THE ANSWER IS " is the argument to the function "operator<<".
- Similarly, to output the number 65, you would code:
-
- cout.operator<<(65) ;
-
- To output a new-line character, you would code:
-
- cout.operator<<('\n') ;
-
- or, if you wish, a string containing nothing but a new-line character:
-
- cout.operator<<("\n") ;
-
- If you really think about it, you should be asking the obvious question,
- "How can a function take a single argument of different types?" In other
- words, in the first call above a string (type "char*") was passed as the
- argument, in the second call an integer (type "int") was passed, and in the
- third a character (type "char") was passed. How can this be? The answer is
- that the function "operator<<" has been "overloaded", so that many versions
- of the "same function" exist within the class "ostream". The compiler is smart
- enough to distinguish one version from another, so that your function call
- correctly accesses the function specifically written to handle the type of
- argument that you provide.
-
- The next obvious question you should be asking is whether you have to
- code a series of such function calls if more than one data item is to be
- output. In other words, in the examples above, are three separate function
- calls really necessary? Fortunately, the answer is no. The way the
- "operator<<" function is written, a reference (address) to the calling
- instance ( cout ) is returned by the function, and can therefore be used as the
- calling instance for a concatenated function call. That is, the three function
- calls can be written:
-
- cout.operator<<("THE ANSWER IS ")
- .operator<<(65)
- .operator<<('\n') ;
-
- That's the good news. The bad news is that it's still too much coding and
- still too awkward. With this in mind, the designers of C++ allow the
- programmer to abbreviate the notation:
-
- cout.operator<<(argument1) ;
-
- with the more convenient:
-
- cout << argument1 ;
-
- and the notation:
-
- cout.operator<<(argument1).operator<<(argument2) ;
-
- with the more convenient:
-
- cout << argument1 << argument2 ;
-
- Note that the dot operator has been eliminated, and the function
- "operator<<" has been replaced with just the insertion operator "<<".
-
- For stylistic purposes you could write each insertion operator on its own
- line. Try running this program.
-
- // EXAMPLE OUTPUT-01
-
- #include <header.h>
-
- int main()
- {
- cout << "THE ANSWER IS "
- << 65
- << "\n" ;
-
- return 0 ;
- }
-
- Only one such occurrence of "cout" needs to be written until the end of the
- statement is reached.
-
- Of course, you could restrict each statement to having just one insertion
- operator. This program produces exactly the same output as before.
-
- // EXAMPLE OUTPUT-02
-
- #include <header.h>
-
- int main()
- {
- cout << "THE ANSWER IS " ;
- cout << 65 ;
- cout << "\n" ;
-
- return 0 ;
- }
-
- In addition to a string, an integer, and a character, the function
- "operator<<" has been overloaded to accept arguments of type "long", "float",
- "double", pointer, and so forth.
-
- ******************************************************************
- Bit Format Flags
-
- Now think about a "printf" function call. It usually consists of a control
- string argument and, optionally, a list of expressions to be output. The
- control string contains literals which will be output exactly as shown, and
- conversion specifications that indicate exactly how an expression from the
- list of expressions is to appear. Each conversion specification starts with a
- '%' and ends with a conversion character, e.g., 'd' for decimal format, 'c' for
- character format, 's' for a string, etc. Between the start and end you may
- enter various flags, the field width, base formatting, justification, floating
- point precision, and so forth. Each conversion specification stands on its
- own; there is no connection to any other one.
-
- In the C++ version 2.0 stream I/O, all of the characteristics relating to how
- an expression should appear apply, not to each individual expression, but to
- the output stream as a whole . In other words, once you specify that decimal
- format is desired, ALL integer numbers from that point on are output in
- decimal format. No further action need be taken. If you decide to switch to
- hexadecimal output, then all integer numbers from that point on will be
- shown in their hex formats. The same is true for floating point precision.
- Once it is set, it stays set for all subsequent floating point numbers. (There
- is one important exception to the "set it and forget it" feature of C++
- streaming that will be discussed later).
-
- Any binary characteristic of the output stream is stored in a long
- (protected) field in the class "ios". In Turbo C++ this field is called
- "x_flags". (The fact that it's protected means that you cannot access it
- directly.) Each characteristic occupies one bit of this field, which simply
- means that it's either true or false; on or off; set or not set. For example,
- the output state of decimal is either on or off. The same can be said for the
- output states of hexadecimal and octal. Also, the state of left-justification
- is either set or not set, as is its opposite, right-justification. On the other
- hand, stream characteristics that require values, such as the field width and
- floating point precision, are stored in integer variables.
-
- Each binary characteristic is represented by a unique value in a field that is
- part of an unnamed public enumerated type in the class "ios". This value is
- represented by exactly one bit in the field. Thus, no two characteristics will
- ever have the same bit position (ranging from 15 down to 0) set on. By
- ORing these bits into the field "x_flags", the different states can be set with
- no conflict. This allows someone examining the field "x_flags" to infer with
- no ambiguity which characteristics are on and which are off.
-
- Each binary characteristic also has a name associated with it that you may
- reference. The complete list of all enumerated values is shown below.
- Because these names exist within the class "ios", the name of this class must
- be specified in conjunction with the scope resolution operator (::) to
- unambiguously access a specific value. These are the names and values for
- Turbo C++:
-
- NAME VALUE MEANING
-
- ios::skipws 0x0001 Skip whitespace on input
- ios::left 0x0002 Left-justification of output
- ios::right 0x0004 Right-justification of output
- ios::internal 0x0008 Pad after sign or base indicator
- ios::dec 0x0010 Show integers in decimal format
- ios::oct 0x0020 Show integers in octal format
- ios::hex 0x0040 Show integers in hexadecimal format
- ios::showbase 0x0080 Show the base for octal and hex numbers
- ios::showpoint 0x0100 Ensure that the decimal point is shown for all
- floating point numbers
- ios::uppercase 0x0200 Show uppercase hex numbers
- ios::showpos 0x0400 Show + for positive numbers
- ios::scientific 0x0800 Use exponential notation on floating point numbers
- ios::fixed 0x1000 Use fixed decimal output on floating point numbers
- ios::unitbuf 0x2000 Flush all streams after insertion
- ios::stdio 0x4000 Flush stdout and stderr after insertion
-
- Because we will be examining these bits very carefully in the coming
- examples, it is very useful to have a function to clearly display the status
- for us. This has been done in the function "FORMATFLAGS" which is
- contained in the file "header.h". This function takes one argument: an
- instance of an output stream. If no argument is supplied, then the function
- defaults to using the name "cout".
-
- Try running this test. Are any of these enumerated bits in the field "x_flags"
- already on when your program first gets control? This little program will
- provide the answer.
-
- // EXAMPLE OUTPUT-11
-
- // TEST THE DEFAULT OF x_flags
-
- #include <header.h>
-
- int main()
- {
- FORMATFLAGS() ;
- }
-
- The output of this program is:
-
- FORMAT FLAGS
- skipws
- unitbuf
-
- The next item of concern is how to turn these settings on and off. Within
- the class "ios" there are several member functions provided that allow this to
- be done. The first of these functions is called "setf". Remember: to call it,
- you must first specify the instance name, "cout", the dot member operator,
- and then the function name. Thus, you would write:
-
- cout.setf
-
- The function "setf" has been overloaded to accept either one or two
- arguments. In both cases, the first argument specifies which bits are to be
- turned on in the field "x_flags".
-
- For example, to turn on the ios::dec bit, you would code:
-
- cout.setf(ios::dec) ;
-
- The function "setf" works by ORing its first argument into the field
- "x_flags", thereby leaving any other bits in this field undisturbed. This means
- that it's possible to turn on more than one bit with just one call to "setf" by
- using an expression for the first argument that contains several bits ORed
- together. For example, to turn on the "ios::dec" and the "ios::right" bits, you
- would code:
-
- cout.setf(ios::dec | ios::right) ;
-
- How can you turn the bits off? Use the member function "unsetf". This
- function takes exactly one argument: the bit pattern to be turned off. Thus,
- to turn off the bit "ios::dec", you would code:
-
- cout.unsetf(ios::dec) ;
-
- Like "setf", more than one bit at a time can be turned off by ORing the
- enumerated values together in the argument field.
-
- Unfortunately, the previous method of turning bits on and off is awkward
- because in many cases the bits are mutually exclusive, and it would
- normally take 2 function calls to (a) turn a bit off using "unsetf", and (b)
- turn another bit on using "setf". Fortunately, a better way exists by using the
- "setf" function with 2 arguments. In this case the second argument represents
- those specific bits which are to be turned OFF prior to having those bits
- turned ON that are specified by the logical AND of the first and second
- arguments. For example, to turn the bit "ios::dec" ON and ensure that the
- mutually exclusive bits "ios::oct" and "ios::hex" are OFF, you would code:
-
- cout.setf(ios::dec , ios::dec | ios::oct | ios::hex) ;
-
- To prove this, run this program:
-
- // EXAMPLE OUTPUT-12
-
- // TEST TURNING ON FORMAT FLAG BITS
-
- #include <header.h>
-
- int main()
- {
- cout.setf(ios::oct | ios::hex) ;
- FORMATFLAGS() ;
- cout.setf(ios::dec , ios::dec | ios::oct | ios::hex) ;
- FORMATFLAGS() ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- FORMAT FLAGS
- skipws
- oct
- hex
- unitbuf
- FORMAT FLAGS
- skipws
- dec
- unitbuf
-
- Because "ios::dec", "ios::oct" and "ios::hex" are mutually exclusive bit fields
- (that is, you only want ONE of them on at any time), you would normally
- AND the one bit of the first argument of "setf" with the OR of all 3 bits, as
- shown above. The second argument can also be specified as "ios::basefield",
- where this value is pre-defined as: "ios::dec | ios::oct | ios::hex".
-
- For example, program OUTPUT-12 can be re-written as:
-
- // EXAMPLE OUTPUT-13
-
- // TEST ios::basefield
-
- #include <header.h>
-
- int main()
- {
- cout.setf(ios::oct | ios::hex) ;
- FORMATFLAGS() ;
- cout.setf(ios::dec , ios::basefield) ;
- FORMATFLAGS() ;
-
- return 0 ;
- }
-
- The output of this program is the same.
-
- In a similar manner, the field "ios::adjustfield" represents the bit positions
- of "ios::left", "ios::right" and "ios::internal" ORed together, and the field
- "ios::floatfield" represents the bit positions of "ios::fixed" and
- "ios::scientific" ORed together.
-
- Because the field "x_flags" in the class "ios" is protected, this means that
- you cannot access it directly. However, there is a public member function
- called "flags" that will return this field to you. If you provide a long integer
- as an argument to "flags", then the existing value of "x_flags" will be
- returned to you after your argument is used to provide a new setting for
- "x_flags".
-
- For example:
-
- // EXAMPLE OUTPUT-14
-
- // TEST THE flags MEMBER FUNCTION
-
- #include <header.h>
-
- int main()
- {
- long value = cout.flags(0) ;
- cout.setf(ios::hex , ios::basefield) ;
- cout << "value is "
- << value
- << '\n' ;
- FORMATFLAGS(cout) ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- value is 2001
- FORMAT FLAGS
- hex
-
- Note that "value" is the old value of "x_flags", and is printed in hexadecimal.
- The '2' represents the "unitbuf" bit, and the '1' is the "skipws" bit. There is
- nothing shown under the heading "FORMAT FLAGS" except "hex" because
- all of the bits were turned off by the call to "flags", then the hex bit was
- turned back on.
-
- Both the "setf" and "unsetf" functions return a value, which you are free to
- use or ignore. The value returned is a long integer representing the
- previous value of the field "x_flags". However, you will probably never
- have occasion to use this value.
-
- ******************************************************************
- The Base Setting and Integer Output
-
- Output formatting is important because you want to have complete
- flexibility in the manner in which your data appears. Let's start with the
- base in which integers will be shown. If a "printf" function call, you have 3
- choices: decimal, octal and hex. A decimal output can be obtained by using
- a conversion specification of "%d" or "%i", an octal by using "%o", and hex by
- using "%x" or "%X". (How to emulate lower vs. upper case will be discussed
- later.) There are 3 bits in the enumerated values shown in the Bit Format
- Flags section that control the base setting:
-
- ios::dec 0x0010 Show integers in decimal format
- ios::oct 0x0020 Show integers in octal format
- ios::hex 0x0040 Show integers in hexadecimal format
-
- To guarantee that decimal output is used, you must turn on the bit
- "ios::dec", and ensure that all the remaining 2 bits are turned off. The same
- reasoning applies to octal and hex output. But we proved in example
- OUTPUT-11 that no bit pertaining to the output base is on by default.
- Therefore, which base will be used? The answer is that the compiler will
- default to decimal output if none of the 3 base field bits is on. But be
- careful! If more than one output base bit happends to be on, then the output
- is unpredictable. (Of course, you would never deliberately put yourself in
- this situation.). Remember: Once the base has been set, it stays set for all
- future integers unless it is subsequently changed.
-
- Recall that the field "ios::basefield" has been defined for you to contain all
- 3 base field bits ORed together, and should be used in the second parameter
- of "setf" to ensure that all 3 bits are turned off before altering them. So now
- let's create a program to output the number 65 using the default base,
- followed by the base in octal, hexadecimal, and decimal. (NOTE: a better
- way in which to write this program will be shown in the section on
- manipulators.)
-
- // EXAMPLE OUTPUT-21
-
- // HOW TO PRINT IN DECIMAL, OCTAL
- // AND HEXADECIMAL FORMATS
-
- #include <header.h>
-
- int main()
- {
- cout << 65 << '\n' ;
- cout.setf(ios::oct , ios::basefield) ;
- cout << 65 << '\n' ;
- cout.setf(ios::hex , ios::basefield) ;
- cout << 65 << '\n' ;
- cout.setf(ios::dec , ios::basefield) ;
- cout << 65 << '\n' ;
-
- return 0 ;
- }
-
- The output of this program, as expected, is:
-
- 65
- 101
- 41
- 65
-
- One final point: In a "printf" function call, the use of the flag # causes the
- base of an octal or hexadecimal number to appear (0 and 0x, respectively).
- The same effect can be achieved in C++ by setting on the bit "ios::showbase".
- Here is example OUTPUT-21 again, but this time the base of the octal and
- hexadecimal numbers is shown. To turn off this feature, use the "unsetf"
- function.
-
- // EXAMPLE OUTPUT-22
-
- // HOW TO PRINT IN DECIMAL, OCTAL
- // AND HEXADECIMAL FORMATS AND SHOW
- // THE BASE FOR OCTAL AND HEX
- // NUMBERS
-
- #include <header.h>
-
- int main()
- {
- cout.setf(ios::showbase) ;
- cout << 65 << '\n' ;
- cout.setf(ios::oct , ios::basefield) ;
- cout << 65 << '\n' ;
- cout.setf(ios::hex , ios::basefield) ;
- cout << 65 << '\n' ;
- cout.setf(ios::dec , ios::basefield) ;
- cout << 65 << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- 65
- 0101
- 0x41
- 65
-
- Note that on positive decimal output, a '+' sign is assumed. If you want this
- sign to appear, turn on the bit "ios::showpos". (Of course, if the number is
- negative, the '-' sign will always appear.) To turn off this feature, use the
- "unsetf" function. In this example the number 65 is displayed with a '+' sign.
-
- // EXAMPLE OUTPUT-23
-
- // HOW TO SHOW THE SIGN OF A POSITIVE
- // NUMBER
-
- #include <header.h>
-
- int main()
- {
- cout.setf(ios::showpos) ;
- cout << 65 << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- +65
-
- There is one other option you can employ with hexadecimal numbers. By
- default any hex digit, as well as the 'x' in the base, appears in lower-case.
- The same rule applies to the 'e' when printing in scientific notation. If you
- want to see upper-case, turn on the bit "ios::uppercase". To revert back to
- lower-case, use the "unsetf" function.
-
- This example prints the number 171 in hexadecimal, and shows all hex
- digits in upper-case.
-
- // EXAMPLE OUTPUT-24
-
- // HOW TO PRINT HEX DIGITS IN
- // UPPER CASE LETTERS
-
- #include <header.h>
-
- int main()
- {
- cout.setf(ios::uppercase | ios::showbase) ;
- cout.setf(ios::hex , ios::basefield) ;
- cout << 171 << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- 0XAB
-
- *****************************************************************
- Character Output
-
- Integer output pertains to the display of decimal, octal and hexadecimal
- numbers. The next question is: Can you emulate "%c" in a "printf" function
- call to set the base for character output, i.e., set it so that all integral
- values appear in their character representations? Unfortunately, the answer
- is no. However, the problem only arises when a non-character value needs to be
- displayed in its character form, because if you define a character using type
- char , then it will automatically be shown in its character format. Thus, to
- make a non-char type appear in character format, an explicit cast to type
- "char" is required.
-
- For example, this program prints the letter 'A' five times:
-
- // EXAMPLE OUTPUT-31
-
- // HOW TO PRINT A CHARACTER AND A
- // NON-CHARACTER IN CHARACTER
- // FORMAT.
-
- #include <header.h>
-
- int main()
- {
- // No cast needed here
- char ch1 = 'A' ;
- cout << ch1 << '\n' ;
- char ch2 = 65 ;
- cout << ch2 << '\n' ;
-
- // Cast needed here
- int ch3 = 65 ;
- cout << (char)ch3 << '\n' ;
- int ch4 = 0101 ;
- cout << (char)ch4 << '\n' ;
- int ch5 = 0x41 ;
- cout << (char)ch5 << '\n' ;
-
- return 0 ;
- }
-
- How about the opposite? That is, suppose you want a character to be shown
- in its decimal, octal, and hexadecimal representations. Once again, a cast is
- required, this time to type "int". By doing this cast in conjunction with the
- proper base setting, the desired result can be obtained. In this example, the
- character "ch" is shown in its decimal, octal, and hexadecimal
- representations.
-
- // EXAMPLE OUTPUT-32
-
- // HOW TO PRINT A CHARACTER AS A
- // DECIMAL, OCTAL AND HEXADECIMAL
- // VALUE.
-
- #include <header.h>
-
- int main()
- {
- char ch = 'A' ;
-
- cout << (int)ch << '\n' ;
- cout.setf(ios::oct , ios::basefield) ;
- cout << (int)ch << '\n' ;
- cout.setf(ios::hex , ios::basefield) ;
- cout << (int)ch << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- 65
- 101
- 41
-
- There is another way to guarantee that an integral value gets shown in its
- character format. That is with the use of the member function called "put"
- (think of the C function "putchar"). This function always outputs its one
- argument in character format, regardless of how it was defined. This
- example prints the character 'A' three times.
-
- // EXAMPLE OUTPUT-33
-
- // HOW TO USE THE MEMBER FUNCTION
- // put TO OUTPUT A CHARACTER
-
- #include <header.h>
-
- int main()
- {
- char ch1 = 'A' ;
- int ch2 = 65 ;
-
- cout.put(ch1) << '\n' ;
- cout.put('A') << '\n' ;
- cout.put(ch2) << '\n' ;
-
- return 0 ;
- }
-
- ******************************************************************
- Setting the Field Width
-
- The field width in C++ works in a similar manner to that of C. If the total
- number of characters needed for output is less than the specified width,
- then the extra spaces will be filled with the current fill character. If the
- number of characters is greater than the specified width, then the width is
- 'expanded' to accommodate the entire field. (In C the fill character in a
- "printf" function call can only be either a zero or a space; in C++ it can be
- any character you desire. This topic is dicussed following width.)
-
- If no width is ever specified, then the default value of zero is assumed (just
- as it is in C). To change the field width, use the member function "width"
- with one argument: the width value itself. Then the next field to be output
- will use this value.
-
- For example, this program prints the number 1 right-justified and preceded
- by 4 blanks, while the number 23 has 3 preceding blanks.
-
- // EXAMPLE OUTPUT-41
-
- // HOW TO SET THE FIELD WIDTH
-
- #include <header.h>
-
- int main()
- {
- cout.width(5) ;
- cout << 1 << '\n' ;
- cout.width(5) ;
- cout << 23 << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- 1
- 23
-
- Something should strike you as odd about this example. Why was it
- necessary to write the line "cout.width(5)" twice? The answer is that the
- width specification only applies to the next field to be output. To prove this
- statement, let's modify this example slightly and remove the second width
- setting.
-
- // EXAMPLE OUTPUT-42
-
- // NOTE THAT THE WIDTH SETTING
- // ONLY APPLIES TO THE NEXT FIELD
- // TO BE OUTPUT
-
- #include <header.h>
-
- int main()
- {
- cout.width(5) ;
- cout << 1 << '\n' ;
- cout << 23 << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- 1
- 23
-
- Now the number 23 appears left-justified because the width reverted back
- to its default value of 0.
-
- In addition to setting the field width, the "width" function also returns the
- value of the width just prior to the function call. If you wish to return this
- value and leave it alone, then call the width function with no argument
- specified.
-
- IMPORTANT NOTE: Under the current implementation of Turbo C++,
- the field width specification does NOT apply to character fields that are
- output. This does not mean that the width reverts back to zero upon
- encountering a character field, but instead is applied to the next non-
- character field that is encountered. The patch IOPAT.ZIP in the
- CompuServe BPROGB forum will fix this problem.
-
- ******************************************************************
- Specifying the Fill Character
-
- If the total number of characters needed to display a field is less than the
- current field width, the extra output spaces will be filled with the current
- fill character. In a "printf" function call, the default fill character is a
- blank, and you only have the option to change it to a zero.
-
- In C++, however, you now have the option for any character to serve as the
- fill character. As before, the default is a blank. The member function "fill"
- is used to specify a new fill character. Once it is specified, it remains as
- the fill character unless it is subsequently changed. The function takes a
- single argument: the new fill character, and returns the previous fill
- character. As with "width", it may be called with no actual argument if you
- merely want to return the previous fill character.
-
- This example outputs the default fill character, changes it to an asterisk, and
- then proves that the current fill character is, indeed, an asterisk.
-
- // EXAMPLE OUTPUT-51
-
- // HOW TO SET THE FILL CHARACTER
-
- #include <header.h>
-
- int main()
- {
- const char quote = '\'' ;
- char old_fill = cout.fill('*') ;
- cout << "Old fill character is "
- << quote
- << old_fill
- << quote
- << '\n' ;
-
- cout << "It was changed to "
- << quote
- << cout.fill()
- << quote
- << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- Old fill character is ' '
- It was changed to '*'
-
- Now let's re-run example OUTPUT-41, but this time we'll fill the first
- field with zeroes, and the second with asterisks.
-
- // EXAMPLE OUTPUT-52
-
- // A COMBINATION OF SETTING THE
- // FIELD WIDTH AND SPECIFYING
- // THE FILL CHARACTER
-
- #include <header.h>
-
- int main()
- {
- cout.width(5) ;
- cout.fill('0') ;
- cout << 1 << '\n' ;
- cout.width(5) ;
- cout.fill('*') ;
- cout << 23 << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- 00001
- ***23
-
- ******************************************************************
- Field Justification
-
- Whenever a field gets output, and the field width is greater than the number
- of characters needed to display the field, the data is always right-justified
- with the fill character used as padding to the left. (Of course, if the field
- width is less than or equal to the number of characters needed, no
- justification occurs and the fill character is ignored.)
-
- Recall that there are 3 bits which are used to set the field justification:
-
- ios::left 0x0002 Left-justification of output
- ios::right 0x0004 Right-justification of output
- ios::internal 0x0008 Pad after sign or base indicator
-
- If no bit is set in the field "x_flags", then the justification defaults to
- right. Once the justification has been set, it remains set unless it is sub-
- sequently changed. As with setting the base, there is a field called
- "ios::adjustfield" which has been defined with all 3 justification bits turned
- on. When setting the justification, this field should be used as the second
- argument in the "setf" member function call to ensure that the other 2 bits are
- turned off. To set the justification to left, use the member function "setf"
- with the bit "ios::left", and to change it back to right, use the bit
- "ios::right".
-
- Here is example OUTPUT-41 again, this time with both fields left-justified.
-
- // EXAMPLE OUTPUT-60
-
- // HOW TO LEFT-JUSTIFY A FIELD
-
- #include <header.h>
-
- int main()
- {
- cout.setf(ios::left , ios::adjustfield) ;
- cout.width(5) ;
- cout.fill('0') ;
- cout << 1 << '\n' ;
- cout.width(5) ;
- cout.fill('*') ;
- cout << 23 << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- 10000
- 23***
-
- The justification resulting from the "ios::internal" bit means that padding
- with the fill character, if any, will occur after the base of the number has
- been shown (for octal and hexadecimal numbers) and before the number
- itself. In the case of decimal numbers, the padding will occur after the sign
- ('+' or '-') and before the number itself. That is, instead of padding
- occurring on the left or on the right, it occurs "in the middle".
-
- In this example, the base is shown, the fill character is set to '=', the
- internal bit is set on, the field width is set to 10, hexadecimal output is
- requested, and the number 65 is printed. Then the same number is printed
- again, but with left justification.
-
- // EXAMPLE OUTPUT-61
-
- // HOW TO DO INTERNAL JUSTIFICATION
-
- #include <header.h>
-
- int main()
- {
- cout.setf(ios::showbase) ;
- cout.fill('=') ;
- cout.setf(ios::internal , ios::adjustfield) ;
- cout.width(10) ;
- cout.setf(ios::hex , ios::basefield) ;
- cout << 65 << '\n' ;
- cout.setf(ios::left , ios::adjustfield) ;
- cout.width(10) ;
- cout << 65 << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- 0x======41
- 0x41======
-
- ******************************************************************
- Floating Point Output
-
- Floating point numbers are output in C++ just like any other type of
- number. However, the formatting is certainly different, and default values
- are not the same as you would get from using a "printf" function call.
-
- In this example some floating point constants are output.
-
- // EXAMPLE OUTPUT-70
-
- // DISPLAY SOME FLOATING POINTS
- // CONSTANTS WITHOUT ANY FORMATTING
-
- #include <header.h>
-
- int main()
- {
- cout << 1.2300 << '\n' ;
- cout << 4.00 << '\n' ;
- cout << 5.678E2 << '\n' ;
- cout << 0.0 << '\n' ;
- }
-
- The output of this program is:
-
- 1.23
- 4
- 567.8
- 0
-
- For the first constant, note that the 2 trailing zeroes were not printed. This
- is certainly different from "printf" in which the default is to show 6
- positions after the decimal point. In the second case, not only do the trailing
- zeroes not show, but even the decimal point does not appear. In the third
- case, the number prints in fixed point notation despite being keyed in
- scientific notation. In the final case, at least one significant digit will
- always appear.
-
- Thus, we can infer that by default, all trailing zeroes, even the decimal
- point, will be suppressed. If you really want to emulate how the "printf"
- function works, you need to turn on the "ios::showpoint" bit. To revert
- back to the default value, use the function "unsetf" to turn it off. Here is
- the same example with this bit now on in the field "x_flags".
-
- // EXAMPLE OUTPUT-71
-
- // HOW TO EMULATE printf
-
- #include <header.h>
-
- int main()
- {
- cout.setf(ios::showpoint) ;
- cout << 1.2300 << '\n' ;
- cout << 4.00 << '\n' ;
- cout << 5.678E2 << '\n' ;
- cout << 0.0 << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- 1.230000
- 4.000000
- 567.800000
- 0.000000
-
- The next step is to override the default of 6 decimal positions. To do this,
- use the member function "precision" in which the one argument is the
- number of decimal positions to be shown. This function also returns the
- previous value of the precision. If it is called without an argument, it
- merely returns the current value of the precision and does not alter it. The
- default precision is 0.
-
- Here is the same example with the precision now set to 1.
-
- // EXAMPLE OUTPUT-72
-
- // HOW TO EMULATE printf AND SET
- // THE PRECISION
-
- #include <header.h>
-
- int main()
- {
- cout.setf(ios::showpoint) ;
- cout.precision(1) ;
- cout << 1.2300 << '\n' ;
- cout << 4.00 << '\n' ;
- cout << 5.678E2 << '\n' ;
- cout << 0.0 << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- 1.2
- 4.0
- 5.7e+02
- 0.0
-
- Note that the third answer is displayed in scientific notation. To guarantee
- that all output is shown in either fixed decimal or scientific notation, recall
- that the following bits are pre-defined in the class "ios":
-
- ios::scientific 0x0800 Use exponential notation on floating
- point numbers
- ios::fixed 0x1000 Use fixed decimal output on floating point
- numbers
-
- If neither bit is turned on, then the compiler emulates the "%g" conversion
- specification in a "printf" function call. Also recall that there is a constant
- called "ios:floatfield" that is the value of these two bits ORed together, and
- may be used as the second argument in a "setf" function call.
-
- // EXAMPLE OUTPUT-73
-
- // HOW TO EMULATE printf AND SET
- // THE PRECISION
-
- #include <header.h>
-
- int main()
- {
- cout.setf(ios::showpoint) ;
- cout.precision(2) ;
-
- // Guarantee fixed decimal
- cout.setf(ios::fixed , ios::floatfield) ;
- cout << 1.2300 << '\n' ;
- cout << 4.00 << '\n' ;
- cout << 5.678E2 << '\n' ;
- cout << 0.0 << '\n' ;
-
- // Guarantee scientific
- cout.setf(ios::scientific , ios::floatfield) ;
- cout << 1.2300 << '\n' ;
- cout << 4.00 << '\n' ;
- cout << 5.678E2<< '\n' ;
- cout << 0.0 << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- 1.23
- 4.00
- 567.80
- 0.00
- 1.23e+00
- 4.00e+00
- 5.68e+02
- 0.00e+00
-
- ******************************************************************
- Printing addresses
-
- The address of a variable (or instance of a class) can be generated by using
- the address operator (&). Because the address operator can be applied to a
- wide variety of types (both built-in and user-defined), the type of argument
- can theoretically be "pointer to int" or "pointer to float" or even 'pointer to
- my class type'. To accommodate all of these various types, the class
- "ostream" contains an overloaded function "operator<<" to handle all such
- argument types. This function is prototyped to accept an argument of type
- "void*" which, according to the rules of argument matching, is acceptable to
- the compiler as a "match".
-
- In Turbo C++, this address is always shown in 32-bit (4 byte) hexadecimal
- form, even though the default output base is decimal .
-
- // EXAMPLE OUTPUT-80
-
- // HOW TO PRINT AN ADDRESS
-
- #include <header.h>
-
- int main()
- {
- int number ;
-
- cout << "Address of number as a\n"
- << " 32-bit hex is "
- << &number
- << '\n' ;
-
- return 0 ;
- }
-
- If you wish, you may view this address in decimal by using a cast to an
- unsigned long.
-
- // EXAMPLE OUTPUT-81
-
- // HOW TO PRINT AN ADDRESS IN
- // DECIMAL AS AN UNSIGNED LONG
-
- #include <header.h>
-
- int main()
- {
- int number ;
-
- cout << "Address of number as a\n"
- << " long integer is "
- << (unsigned long)&number
- << '\n' ;
-
- return 0 ;
- }
-
- But since addresses are stored in 2 bytes when using the small memory
- model, it's probably better to cast to an "unsigned int".
-
- // EXAMPLE OUTPUT-82
-
- // HOW TO PRINT AN ADDRESS IN
- // DECIMAL AS AN UNSIGNED INT
-
- #include <header.h>
-
- int main()
- {
- int number ;
-
- cout << "Address of number as an\n"
- << " unsigned int is "
- << (unsigned)&number
- << '\n' ;
-
- return 0 ;
- }
-
- When dealing with a string, a problem arises. It's the same problem that
- occurred in C in a program fragment such as:
-
- char* ptr = "ABC" ;
- printf ("%s" , ptr) ;
-
- No doubt the user will see "ABC" as the output. The problem is how to print
- the ADDRESS contained within the pointer variable "ptr". The solution in C is
- to provide a different conversion specification, namely "%p".
-
- In C++ this program fragment:
-
- char* ptr = "ABC" ;
- cout << ptr ;
-
- would also output "ABC" because the argument "ptr" is of type "char*" which
- matches exactly an overloaded "operator<<" that accepts an argument of
- type "char*". To emulate the "%p" in C++, you must override the built-in
- type of "char*" with the type "void*" so that the "operator<<" function that
- outputs an ADDRESS will be called instead.
-
- // EXAMPLE OUTPUT-83
-
- // HOW TO PRINT AN ADDRESS OF
- // A STRING
-
- #include <header.h>
-
- int main()
- {
- char* ptr = "ABC" ;
-
- cout << "The string itself is "
- << ptr
- << '\n' ;
-
- cout << "The address of the"
- << "string is "
- << (unsigned)(void*)ptr
- << '\n' ;
-
- return 0 ;
- }
-
- ******************************************************************
- Binary output
-
- It's possible to take any internal representation of a C++ type and output it
- as though it were just an array of characters. For a type such as "float", this
- will produce meaningless output, but it may be useful for integers. To do
- this, the member function write must be used. This function takes 2
- arguments: The first is the address of the data to be output, and the second
- is the number of bytes to be shown. Note that in the case of a string, the
- null byte is treated just like any other byte.
-
- Because the function "write" is declared to accept an argument of type
- "char*", if the item you wish to print is not of this type, then the address
- must be generated using the address operator, and then cast to type "char*".
-
- // EXAMPLE OUTPUT-90
-
- // HOW TO DISPLAY THE BINARY REPRE-
- // SENTATION OF A NUMBER
-
- #include <header.h>
-
- int main()
- {
- long number = 0x414243 ;
-
- cout.write((char*)&number , sizeof(number)) ;
- cout << '\n' ;
-
- return 0 ;
- }
-
- Note that the output of this program is "CBA", because the address of a "long"
- refers to the low-order byte.
-
- ************************ SECTION 3 -- INPUT *********************************
-
- Introduction
-
- In addition to being able to use classes to control output, C++ stream I/O
- classes also handle all input. Just as output consists of a stream of characters
- being sent to some device, input consists of characters coming in from some
- device and being translated into their proper defined type. In other words,
- the characters '1', '2' and '3' could be the string '123' or the integer 123,
- depending upon the type of the receiving field. Unlike output, the realm of
- possibilities for 'formatting' simply does not exist when inputting data.
-
- The class "istream" is derived from the class "ios", and it controls the input
- handling functions. The global instance that is defined for you is called "cin",
- and is defined as:
-
- istream cin ;
-
- In addition to data members, the class "istream" contains functions to
- operate upon these members. The function you will use the most often is
- called the extraction operator, and it is written as:
-
- operator>>
-
- The argument to this function is the variable name that you wish to contain
- the input. The name 'extraction' comes from the fact that you are
- 'extracting' (taking) data from the input stream.
-
- For example, to input an integer, you would code:
-
- int number ;
- cin.operator>>(number) ;
-
- As with the insertion operator, this code can be replaced with the simpler
- form:
-
- int number ;
- cin >> number ;
-
- Note that the type of the input variable determines how the characters from
- the input stream are to be stored.
-
- For example, this program inputs a number, character and float, and then
- echoes them back.
-
- // EXAMPLE INPUT-01
-
- // HOW TO INPUT SOME SIMPLE TYPES
-
- #include <header.h>
-
- int main()
- {
- int number ;
- cout << "Enter a number: " ;
- cin >> number ;
- cout << "You entered: "
- << number
- << '\n' ;
-
- char ch ;
- cout << "Enter a character: " ;
- cin >> ch ;
- cout << "You entered: "
- << ch
- << '\n' ;
-
- float fl ;
- cout << "Enter a float: " ;
- cin >> fl ;
- cout << "You entered: "
- << fl
- << '\n' ;
-
- return 0 ;
- }
-
- The extraction operator, like the insertion operator, can be chained
- together.
-
- // EXAMPLE INPUT-02
-
- // EXTRACTION OPERATORS CAN BE
- // CHAINED TOGETHER
-
- #include <header.h>
-
- int main()
- {
- int number1 , number2 ;
- cout << "Enter 2 numbers: " ;
- cin >> number1 >> number2 ;
- cout << "You entered: "
- << number1
- << " and "
- << number2
- << '\n' ;
-
- return 0 ;
- }
-
- Recall from the discussion on output that numbers can be displayed in
- either their decimal, octal, or hexadecimal representations. This is done by
- changing the base setting of the output stream. In a similar fashion, the base
- of the input stream can be changed from its default setting of decimal to
- either octal or hexadecimal. This is how the "%o" and "%x" conversion
- specifications in a "scanf" function call can be emulated. For example, this
- program prompts the user for numbers in decimal, octal, and hexadecimal
- formats, then echoes each number back. Note that the output is always in
- decimal because the setting of the output base has not been affected. In
- other words, the base setting is stored separately for each stream.
- Obviously, if an illegal input value is entered, the value will not be stored.
- (NOTE: Borland Turbo C++ version 1.00 had a bug in regard to the
- inputting of octal and hex numbers, but it was corrected in version 1.01.)
-
- // EXAMPLE INPUT-03
-
- // INTEGERS CAN BE INPUT IN ANY OF
- // 3 DIFFERENT BASES
-
- #include <header.h>
-
- int main()
- {
- int number ;
-
- cout << "Enter a decimal number: " ;
- cin >> number ;
- cout << "You entered: "
- << number
- << endl ;
-
- cout << "Enter an octal number: " ;
- cin.setf(ios::oct , ios::basefield) ;
- cin >> number ;
- cout << "You entered: "
- << number
- << endl ;
-
- cout << "Enter a hex number: " ;
- cin.setf(ios::hex , ios::basefield) ;
- cin >> number ;
- cout << "You entered: "
- << number
- << endl ;
-
- return 0 ;
- }
-
- If the operator enters 65 for all 3 prompts, then the output of this program
- is:
-
- 65
- 53
- 101
-
- *****************************************************************
- Character Input
-
- Characters may be read in using either the extraction operator ">>" or the
- member function "get" (think of the C function "getchar"). If the extraction
- operator is used, then leading whitespace is bypassed and the first non-
- whitespace character is fetched. Also, a reference to the invoking instance is
- returned. In this example, enter some spaces and tabs before striking a
- character and <RETURN>.
-
- // EXAMPLE INPUT-11
-
- // DEMONSTRATE HOW TO READ IN A
- // CHARACTER AND BYPASS LEADING
- // WHITESPACE
-
- #include <header.h>
-
- int main()
- {
- const char quote = '\'' ;
-
- cout << "Enter a character: " ;
- char ch ;
- cin >> ch ;
- cout << "You entered: "
- << quote
- << ch
- << quote
- << '\n' ;
-
- return 0 ;
- }
-
- Another method to read a character is to use the member function "get". This
- can be done in two differenct ways. The first takes one argument: the
- character variable name pass in by reference. It returns a reference to the
- invoking instance. The difference between the extraction operator ">>" and
- "get" is that "get" does not use the format flags, so it does not bypass
- leading whitespace. In this example, enter some spaces and then another
- character. You will see that the first space gets entered into the variable.
-
- // EXAMPLE INPUT-12
-
- // DEMONSTRATE HOW TO READ IN A
- // CHARACTER AND RETAIN LEADING
- // WHITESPACE USING get(char&)
-
- #include <header.h>
-
- int main()
- {
- const char quote = '\'' ;
-
- cout << "Enter a character: " ;
- char ch ;
- cin.get(ch) ;
- cout << "You entered: "
- << quote
- << ch
- << quote
- << '\n' ;
-
- return 0 ;
- }
-
- The other function using "get" takes no input argument (just like "getchar" in
- C) and returns a value of type "int", which represents the character just read,
- or the "EOF" constant if either (a) end-of-file was detected, or (b) no
- character could be read. This function is unique in that it does not set
- 'failbit'. The other unformatted extractors will set 'failbit' if called at
- end-of-file time, so that no other characters can be read.
-
- Here is example INPUT-12 repeated, but now it uses "get()" and checks for
- end-of-file. Because the variable "ch" must be defined as type "int", don't
- forget the cast in order to display it as a character.
-
- // EXAMPLE INPUT-13
-
- // DEMONSTRATE HOW TO READ IN A
- // CHARACTER AND RETAIN LEADING
- // WHITESPACE USING get()
-
- #include <header.h>
-
- int main()
- {
- const char quote = '\'' ;
-
- cout << "Enter a character: " ;
- int ch ;
- ch = cin.get() ;
- if (ch != EOF)
- cout << "You entered: "
- << quote
- << (char)ch
- << quote
- << '\n' ;
- else
- cout << "End-of-file\n" ;
-
- return 0 ;
- }
-
- ******************************************************************
- String Input
-
- As with character input, strings may be entered using the extraction
- operator or the overloaded member function "get". With the extraction
- operator, leading whitespace is bypassed, and the first whitespace
- encountered terminates the input. (This acts just like the function "scanf".)
-
- // EXAMPLE INPUT-21
-
- // DEMONSTRATE HOW TO READ IN A
- // STRING AND BYPASS WHITESPACE
- // TOTALLY (LOOKS LIKE scanf)
-
- #include <header.h>
-
- const int length = 100 ;
-
- int main()
- {
- char string [length] ;
- const char quote = '\"' ;
-
- cout << "Enter a string: " ;
- cin >> string ;
- cout << "Your string: "
- << quote
- << string
- << quote
- << '\n' ;
-
- return 0 ;
- }
-
- But just like "scanf", the possibility exists for a program hang or crash if
- the operator enters more characters than can safely be accommodated by the
- string array. In other words, run this program and enter more than 10
- characters.
-
- // EXAMPLE INPUT-22
-
- // IT'S POSSIBLE TO OVERFLOW AN
- // ARRAY WHEN INPUTTING A STRING
-
- #include <header.h>
-
- const int max = 10 ;
-
- int main()
- {
- char array[max] ;
-
- cout << "Enter a string: " ;
- cin >> array ;
- cout << "You entered: "
- << array
- << '\n' ;
-
- return 0 ;
- }
-
- If it didn't bomb, consider yourself lucky. To guard against this disaster,
- you may set the width of the input stream to physically limit the number of
- characters that will be stored. This is done by using the member function
- "width" in the class "istream". Run this program and enter the letters 'A'
- through 'M'. Note that only the letters 'A' through 'I' got stored into the
- string (the last byte is always reserved for the null character) and the
- remaining characters remain in the input buffer.
-
- // EXAMPLE INPUT-23
-
- // HOW TO AVOID OVERFLOWING AN
- // ARRAY WHEN INPUTTING A STRING
- // WITH THE EXTRACTION OPERATOR
-
- #include <header.h>
-
- const int max = 10 ;
-
- int main()
- {
- char array[max] ;
-
- cout << "Enter a string: " ;
- cin.width(max) ;
- cin >> array ;
- cout << "You entered: "
- << array
- << '\n' ;
-
- return 0 ;
- }
-
- Caution: Just like the "width" function used for output, the input "width"
- function only applies to the next item to be input.
-
- Another way to read in strings is to use the member function get with 3
- arguments. (Note the similarity to the C function "fgets".) The first
- argument is the address of the string area, the second is the maximum
- number of characters (less 1) than can be read in, and the third specifies the
- terminating character (the one that will stop the transfer of characters from
- the buffer into the string array). This third argument defaults to the value
- '\n', which is the <RETURN> key. Note, however, that if it is changed to
- some other character, then the <RETURN> key must still be pressed for
- the input action to cease. Both leading whitespace and embedded
- whitespace are retained as part of the string value.
-
- // EXAMPLE INPUT-24
-
- // DEMONSTRATE HOW TO READ IN A
- // STRING AND RETAIN WHITESPACE,
- // BOTH LEADING AND EMBEDDED
-
- #include <header.h>
-
- const int length = 100 ;
-
- int main()
- {
- char string [length] ;
- const char quote = '\"' ;
-
- cout << "Enter a string: " ;
- cin.get(string , length) ;
- cout << "Your string: "
- << quote
- << string
- << quote
- << '\n' ;
-
- return 0 ;
- }
-
- A slight variation on the function "get" taking 3 arguments is the function
- "getline". The only difference is that "getline" extracts the newline character
- ('\n') from the input buffer, whereas "get" leaves it alone (and, presumably,
- must then be flushed by the programmer). In Turbo C++ version 1.01, this
- newline character is made part of the string, even though the A T & T spec
- does not say to do this.
-
- // EXAMPLE INPUT-25
-
- // TEST getline FUNCTION
-
- // NOTE THAT THE LAST DOUBLE QUOTE
- // APPEARS ON THE FOLLOWING LINE
- // DUE TO THE INCLUSION OF THE
- // NEWLINE CHARACTER IN string
-
- #include <header.h>
-
- const int length = 100 ;
-
- int main()
- {
- char string [length] ;
- const char quote = '\"' ;
-
- cout << "Enter a string: " ;
- cin.getline(string , length) ;
- cout << "Your string: "
- << quote
- << string
- << quote ;
-
- return 0 ;
- }
-
- ******************************************************************
- Checking for End-Of-File
-
- When reading data from the keyboard or a file, the programmer must
- always be on guard for the occurrence of an end-of-file mark (in DOS it's
- the character ^Z or decimal value 26 from text files). This is comparable to
- detecting the value "EOF" when doing a "scanf" in C.
-
- In C++, the member function "eof" in the class "istream" taking no arguments
- will report 'true' if the end-of-file condition was found, 'false' if not
- found.
-
- Normally data is obtained within a 'while' loop, with the loop continuing to
- execute as long as end-of-file is not detected. This situation could be coded
- like this:
-
- // EXAMPLE INPUT-31
-
- // ENTER A NUMBER AND LOOP
- // UNTIL EOF IS DETECTED
-
- #include <header.h>
-
- int main()
- {
- cout << "Enter a number: " ;
- int num ;
- cin >> num ;
- while (!cin.eof())
- {
- cout << "You entered: "
- << num
- << '\n' ;
- cout << "\nNext number: " ;
- cin >> num ;
- }
-
- return 0 ;
- }
-
- While this program certainly works, there is a better way to code it. Since
- the statement:
-
- cin >> num ;
-
- returns a reference to the invoking object itself (namely "cin"), this object
- can be used as the invoking object for the "eof" member function call. Thus,
- the revised code looks like:
-
- // EXAMPLE INPUT-32
-
- // A BETTER METHOD OF CODING
- // EXAMPLE INPUT-31
-
- #include <header.h>
-
- int main()
- {
- cout << "Enter a number: " ;
- int num ;
- while (!(cin >> num).eof())
- {
- cout << "You entered: "
- << num
- << '\n' ;
- cout << "\nNext number: " ;
- }
-
- return 0 ;
- }
-
- Notice how the read and check for end-of-file have been combined to form
- the Boolean condition of the 'while' loop.
-
- ******************************************************************
- Checking for Errors
-
- Unfortunately, we live in an imperfect world. People don't smile, cars
- crash, checks bounce, and data entry operators don't always do what they're
- supposed to do. This means that as a programmer you must be responsible
- for making your code as 'operator-proof' as possible. In other words, no
- matter what the user may enter as 'data', your program must capture it and
- successfully trap all error conditions to avoid such catastrophes as 'garbage
- in, garbage out', aborts, hangs, endless loops, etc.
-
- When expecting character or string input, there's not too much that can go
- wrong, other than array overflow which has already been covered. But
- with numeric data, such as "int"s, "float"s and "double"s, only certain
- keystrokes in a prescribed order and considered to be valid. For example,
- when you expect a decimal integer to be input, the user may enter a sign (+
- or -) followed by the digits 0 through 9. An entry of A12 is obviously
- invalid. An entry such as 12A, however, is considered to be the number 12,
- with the "invalid" character 'A' serving to terminate the numeric portion of
- the input stream. In addition, any whitespace character terminates a
- numeric entry, and all leading whitespace characters are bypassed
- automatically.
-
- There are several ways to check for 'garbage' input when expecting valid
- numeric data. Within the class "istream" the member function "good" will
- return 'true' if the preceding operation succeeded, 'false' otherwise. In
- addition, the member function "fail" will do just the opposite.
-
- Another way to check for an input error is to use the overloaded "operator!"
- (Boolean not) on the instance of "istream". This operator will return
- 'true' if an error occurred, 'false' otherwise. Similarly, testing the
- instance itself as a Boolean value will return 'true' if the input was good,
- 'false' otherwise.
-
- Note that in the header file <header.h> the function "IOFLAGS" has been
- defined to accept an instance of an input stream as an argument, and will
- display the various input states. The default argument is the instance "cin".
- To test this, trying entering some numeric and non-numeric data for this
- program, as well as end-of-file.
-
- // EXAMPLE INPUT-41
-
- // TEST THE INPUT STREAM STATES
-
- #include <header.h>
-
- int main()
- {
- cout << "Enter a number: " ;
- int number ;
- cin >> number ;
- IOFLAGS() ;
-
- return 0 ;
- }
-
- Now that you see what does and does not work, the next problem is how to
- eliminate the excess, or garbage, characters from the input stream. This can
- be accomplished by reading 1 character at a time until the <RETURN>
- ('\n') character has been read. (Of course this method assumes that if an
- error condition is encountered, then the integrity of the remaining
- characters in the buffer is in question.) In addition, when an error occurs,
- the status of the input stream is changed from 'good' to 'fail' and no more
- characters can be read until the status is reset to 'good' . To do this, you
- must call upon the "istream" member function "clear" with no arguments (the
- default argument is 0, which means 'set the status of the stream to 'good'').
-
- To save you the trouble of having to code a function to accomplish this
- 'flushing' task, the file <header.h> has a manipulator called FLUSH that will
- do this for you. (The subject of manipulators and how they work will be
- covered in the section on manipulators.)
-
- Thus, an 'operator-proof' program that loops while reading numbers and
- checking for end-of-file and garbage input might resemble this:
-
- // EXAMPLE INPUT-42
-
- // SHOW HOW TO HANDLE ANYTHING
- // THE OPERATOR CAN THROW AT
- // THE PROGRAM
-
- #include <header.h>
-
- int main()
- {
- cout << "\nEnter a number: " ;
- int number ;
- while (!(cin >> number).eof())
- {
- // Test for a bad number
- if (!cin)
- cout << "Input error!\n" ;
-
- // Process a good number
- else
- cout << "YOU ENTERED: "
- << number
- << endl ;
-
- // Clear out the input buffer.
- cin >> FLUSH ;
-
- cout << "\nNext number: " ;
- }
- cout << "\nEND OF PROGRAM\n" ;
-
- return 0 ;
- }
-
- One note about the logic of this program. If 2 valid numbers are entered
- before the user presses <RETURN>, then only the first number will be
- processed; the second will be flushed. You may or may not want this to
- happen, depending upon your design philosophy.
-
- ************************ SECTION 4 -- MANIPULATORS ************************
-
- Introduction
-
- As you have probably noticed by now, the elimination of conversion
- specifications ("%d", "%c", etc.) found in the "scanf" and "printf" functions
- in C causes you to do a lot of extra coding to accomplish the same end result.
- For example, to output a number in hex, the specification "%x" does the job
- in a "printf" statement, but with <iostream.h> you have to write:
-
- cout.setf(ios::hex , ios::basefield) ;
-
- before outputting the number itself.
-
- Fortunately, the input and output stream classes provide the capability to
- eliminate much of this tedious coding, as well as a method to combine the
- setting of the stream state with the actual outputting (or inputting) of the
- data itself. This method uses what is called a 'manipulator'. The term
- comes from the fact that a manipulator does just what it implies: it
- manipulates, or changes, the state of the stream.
-
- In the same sense that a call of a function causes that function to do any
- number of individual tasks the programmer may specify, a manipulator is
- also a function that sets the stream state. The input and output classes come
- with some manipulators already built in, and a nice feature is that you can
- easily define your own.
-
- The one peculiar aspect about manipulators is that they are always called
- 'indirectly' by a separate function. This other function knows which
- individual manipulator to call because it has the manipulator's address as
- its one argument. How does it get this address? When the name of a function is
- written without the open and close parentheses, the compiler generates the
- address of the function instead of actually calling the function. This address
- can, in turn, be passed as an argument to another function. Of course, this
- address must be stored into a variable declared as 'pointer to function' and
- which has the proper return type and argument list.
-
- In this example the address of the function "print" is passed as an argument
- to the function called "test", stored into the pointer variable called "ptr",
- and then executed via the statement "ptr();". Note that this is the same as
- coding (*ptr)();
-
- // EXAMPLE MANIP-01
-
- // HOW TO PASS THE ADDRESS OF A
- // FUNCTION AS AN ARGUMENT
-
- #include <header.h>
-
- // Function declarations
-
- void print() ;
- void test(void (*)()) ;
-
- int main()
- {
- test(print) ;
-
- return 0 ;
- }
-
- void print()
- {
- cout << "print function\n" ;
- }
-
- void test(void (*ptr)())
- {
- ptr() ;
- }
-
- Since the manipulators to handle input and output stream states can be
- chained with other arguments, then (assuming output) generically they have
- the form:
-
- cout << data-item1 << manipulator << data-item2 ;
-
- In order to preserve the ability to concatenate successive calls to the
- insertion operator function, each manipulator function must adhere to the
- rule that the invoking instance is always passed in by reference and
- returned by reference. This means that all manipulator functions have the
- format:
-
- ostream& manipulator_name(ostream& strm)
- {
- // your code here
- return strm ;
- }
-
- In a similar fashion, all input manipulators have the format:
-
- istream& manipulator_name(istream& strm)
- {
- // your code here
- return strm ;
- }
-
- To accommodate an argument of type 'pointer to function', the class
- "ostream" has an overloaded insertion operator similar to this:
-
- ostream& operator<<(ostream& (*ptr)(ostream&))
- {
- return (*ptr)(*this) ;
- }
-
- The class istream has an overloaded insertion operator similar to this:
-
- istream& operator<<(istream& (*ptr)(istream&))
- {
- return (*ptr)(*this) ;
- }
-
- It's these two functions that call upon your manipulator function whose
- address has been stored into the pointer variable "ptr".
-
- As a test, here is a program that creates a manipulator function called
- "manip" that sets the field width to 5 and the fill character to '*'.
-
- // EXAMPLE MANIP-02
-
- // HOW TO PASS CREATE YOUR OWN
- // MANIPULATOR
-
- #include <header.h>
-
- ostream& manip(ostream& strm)
- {
- strm.width(5) ;
- strm.fill('*') ;
- return strm ;
- }
-
- int main()
- {
- cout << manip
- << 23
- << '\n' ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- ***23
-
- ******************************************************************
- Built-in Manipulators Taking No Arguments
-
- Because some output (and input) stream manipulations are done so
- frequently, the header file <iostream.h> includes some pre-defined
- manipulators. These manipulators may take no arguments, or 1 argument.
- Let's first consider some built-in manipulators that take no arguments.
-
- As the first example, the manipulator "endl" is designed to output a new line
- character and flush the output buffer. Use may use this in place of '\n'. For
- example,
-
- // EXAMPLE MANIP-11
-
- // THE endl MANIPULATOR
-
- #include <header.h>
-
- int main()
- {
- cout << 1
- << endl
- << 2
- << endl ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- 1
- 2
-
- The manipulators "dec", "oct" and "hex" work for both input and output
- operations, and set the stream state accordingly. For example,
-
- // EXAMPLE MANIP-12
-
- // SETTING THE STREAM STATES USING
- // MANIPULATORS
-
- #include <header.h>
-
- int main()
- {
- cout << "Input a hex number: " ;
- int number ;
- cin >> hex >> number ;
- cout << "The number in octal is "
- << oct
- << number
- << endl ;
-
- return 0 ;
- }
-
- If you enter the number '1f' (hex), you will then see the number 37 (octal).
-
- Recall that when using the function "get" with 3 arguments to read in a
- string, both leading and embedded whitespace are retained. If you want to
- bypass the leading whitespace, and still retain the embedded whitespace,
- then use the input manipulator "ws". In this program enter some leading
- whitespace, then some significant characters including embedded
- whitespace.
-
- // EXAMPLE MANIP-13
-
- // DEMONSTRATE HOW TO READ IN A
- // STRING AND RETAIN EMBEDDED
- // WHITE SPACE, BUT BYPASS
- // LEADING WHITE SPACE
-
- #include <header.h>
-
- const int length = 100 ;
-
- int main()
- {
- char string [length] ;
- const char quote = '\"' ;
-
- cout << "Enter a string: " ;
- cin >> ws ;
- cin.get(string , length) ;
- cout << "Your string: "
- << quote
- << string
- << quote
- << endl ;
-
- return 0 ;
- }
-
- There is one other built-in manipulator that takes no argument: "ends". This
- causes a null character to be output, and is useful for objects of type
- strstream .
-
- ******************************************************************
- Built-in Manipulators Taking 1 Argument
-
- Because a manipulator that takes 1 argument requires special handling by
- the compiler, a separate header file must be included with each program.
- This file is called <iomanip.h>. Note that in the file <header.h> it is
- automatically included.
-
- Perhaps the most frequently used manipulator taking an argument is "setw".
- Like its counterpart, the member function "width()", it is used to set the
- field width for the next output item only. But since manipulators are
- designed to be coded 'in line' with data to be output, they do not return a
- value (which would cause spurious data to appear in the output buffer). The
- one argument is, of course, the field width itself.
-
- Here is example OUTPUT-41 again, this time using a manipulator to set the
- field width.
-
- // EXAMPLE MANIP-21
-
- // HOW TO SET THE FIELD WIDTH
- // USING A MANIPULATOR
-
- #include <header.h>
-
- int main()
- {
- cout << setw(5)
- << 1
- << endl ;
- cout << setw(5)
- << 23
- << endl ;
-
- return 0 ;
- }
-
- Another frequently used manipulator is the one that sets the fill character.
- It is called "setfill" and, as you would expect, the 1 argument is the fill
- character itself.
-
- Here is example OUTPUT-52 again, this time using manipulators to set the
- field width and fill character.
-
- // EXAMPLE MANIP-22
-
- // A COMBINATION OF SETTING THE
- // FIELD WIDTH AND SPECIFYING
- // THE FILL CHARACTER USING
- // MANIPULATORS
-
- #include <header.h>
-
- int main()
- {
- cout << setw(5)
- << setfill('0')
- << 1
- << endl ;
- cout << setw(5)
- << setfill('*')
- << 23
- << endl ;
-
- return 0 ;
- }
-
- The other manipulators that take an argument are:
-
- resetiosflags(long flag) -- turns off the bits specified in "flag"
- (input and output)
- setbase(int base) -- sets the output base to decimal if "base" if 0
- or 10; to octal if "base" is 8; to hexadecimal
- if "base" is 16 (output)
- setiosflags(long flag) -- turns on the bits specified in "flag" (input
- and output)
- setprecision(int prec) -- sets the number of digits displayed after the
- decimal point to "prec" (output)
-
- ******************************************************************
- Creating Your Own Manipulators Taking 1 Argument
-
- The explanation of how manipulators taking 1 argument are handled by the
- compiler is too complex to be explained here, so let's just see how it is
- coded.
-
- First, the generic form (assuming output) of the manipulator is:
-
- ostream& manipulator-name(ostream& strm , type arg)
- {
- // your code here using arg
- return strm ;
- }
-
- where "type" is either "int" or "long", and "arg" is the formal argument name.
-
- Next, you must include this code:
-
- OMANIP(type) manipulator-name(type arg)
- {
- return OMANIP(type) (manipulator-name , arg) ;
- }
-
- where "OMANIP" is a class defined in <iomanip.h>.
-
- For example, here is a manipulator called "manip" that sets the field width to
- whatever the argument happens to be, and also sets the fill character to '*'.
-
- // EXAMPLE MANIP-31
-
- // HOW TO CREATE A MANIPULATOR THAT
- // TAKES 1 ARGUMENT
-
- #include <header.h>
-
- ostream& manip(ostream& strm , int length)
- {
- strm << setw(length) << setfill('*') ;
- return strm ;
- }
-
- OMANIP(int) manip(int length)
- {
- return OMANIP(int) (manip , length) ;
- }
-
- int main()
- {
- cout << manip(7)
- << 123
- << endl ;
- cout << manip(5)
- << 45
- << endl ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- ****123
- ***45
-
- If you wish to use a type other than "int" or "long", then you must include the
- following statement:
-
- IOMANIPdeclare(type) ;
-
- where "type" is either "char", "float", "double", etc.
-
- Here is the same example, but now it is the fill character that is variable.
-
- // EXAMPLE MANIP-32
-
- // HOW TO CREATE A MANIPULATOR THAT
- // TAKES 1 ARGUMENT, AND THAT ARGUMENT
- // IS NOT int OR long
-
- #include <header.h>
-
- // Don't forget this statement
- IOMANIPdeclare(char) ;
-
- ostream& manip(ostream& strm , char ch)
- {
- strm << setw(7) << setfill(ch) ;
- return strm ;
- }
-
- OMANIP(char) manip(char ch)
- {
- return OMANIP(char) (manip , ch) ;
- }
-
- int main()
- {
- cout << manip('*')
- << 123
- << endl ;
- cout << manip('$')
- << 45
- << endl ;
-
- return 0 ;
- }
-
- The output of this program is:
-
- ****123
- $$$$$45
-
- Finally, take a look at the file <header.h> for some useful manipulators that
- have already been defined for you.
-
- ************************* SECTION 5 -- FILE I/O ****************************
-
- Introduction
-
- File input/output using <iostream.h> involves any of these 3 operations:
-
- 1) Reading a file
- 2) Writing a file
- 3) Both reading and writing a file
-
- To handle these operations, special classes have already been defined. They
- are:
-
- Read -- class ifstream (derived from istream )
- Write -- class ofstream (derived from ostream )
- Both -- class "fstream" (derived from iostream )
-
- To use any of these classes, you must:
-
- #include <fstream.h>
-
- which automatically includes the header file <iostream.h>. (The file
- <fstream.h> is already included in <header.h>.)
-
- There are no pre-defined instances of these classes comparable to "cin" and
- "cout". Therefore, the first step in using file I/O is to create an instance of the
- appropriate class, e.g.,
-
- ifstream file_in ;
- ofstream file_out ;
- fstream file_both ;
-
- ****************************************************************
- Using the instances
-
- The first step in using the file instances is to open the disk file. In any
- computer language this means establishing a communication link between
- your code and the external file. Each of the 3 classes provides the member
- function called open to do this. The declarations for these open functions
- are as follows:
-
- void ifstream::open(const char* name ,
- int m = ios::in ,
- int prot = filebuf::openprot) ;
-
- void ofstream::open(const char* name ,
- int m = ios::out ,
- int prot = filebuf::openprot) ;
-
- void fstream::open(const char* name ,
- int m,
- int prot = filebuf::openprot) ;
-
- The first argument is the external file name passed in as a constant string
- literal.
-
- The second argument is the file mode, and comes from a public
- enumerated type in the class "ios". There are eight possible modes, as
- follows:
-
- ios::in Input mode. (Default for input file.)
- ios::out Output mode. (Default for output file.)
- ios::app Append to an output file rather than update an existing
- record.
- ios::ate Position file marker at end of file instead of beginning.
- ios::trunc Delete file if it exists and re-create it.
- ios::nocreate File must exist, otherwise open fails (output only)
- ios::noreplace File must not exist, otherwise open fails (output only)
- ios::binary Binary mode; default is text (Binary is a Borland
- enhancement)
-
- Note that for "fstream" instances, there is no default mode. Obviously, these
- various modes may be bitwise ORed together if more than one is desired.
-
- The third argument is the file access. Under Turbo C++ version 1.01, the
- possible values are:
-
- 0 = Default
- 1 = Read-only file
- 2 = Hidden file
- 4 = System file
- 8 = Archive bit set
-
- If the open fails, the overloaded "operator!" used on the instance will
- return 'true'.
-
- For example:
-
- file_in.open("INPUT") ;
- file_out.open("OUTPUT") ;
- file_both.open("BOTH" , ios::in | ios::out) ;
-
- An alternate method of calling the open function is to call the constructor
- with the same argument(s) that you would use for the "open". Thus, instead
- of creating the instance and then explicitly calling the "open" function, you
- can combine these two steps by writing:
-
- ifstream file_in("INPUT") ;
- ofstream file_out("OUTPUT") ;
- fstream file_both("BOTH", ios::in | ios::out) ;
-
- When you are done using the file, the member function "close" taking no
- arguments will close it. This function is called automatically by the
- destructor for the class, but you may call it explicitly if you wish.
-
- Let's start with a simple program that accepts string input from the user
- and writes it to a disk file called "OUTPUT.DAT".
-
- // EXAMPLE FILEIO-01
-
- // CREATE AN OUTPUT FILE AND WRITE
- // WHATEVER DATA THE OPERATOR MAY
- // ENTER
-
- #include <header.h>
-
- const int max = 100 ;
-
- ////////////////////////////////////
-
- int main()
- {
- char buffer[max] ;
- ofstream file_out("OUTPUT.DAT") ;
- if (!file_out)
- {
- cout << "OPEN FAILED\n" ;
- PAUSE() ;
- exit(1) ;
- }
-
- cout << "Enter a line of data: " ;
- while (!cin.get(buffer , max).eof())
- {
- file_out << buffer << endl ;
- cout << "Next line: " ;
- cin >> FLUSH ;
- }
-
- return 0 ;
- }
-
- Now give the user a chance to append more records to the file. Note that
- the mode of the file is "ios::out | ios::app" (although "ios::app" by itself
- would still have worked).
-
- // EXAMPLE FILEIO-02
-
- // GIVE THE USER A CHANCE TO APPEND
- // RECORDS TO THE FILE
-
- #include <header.h>
-
- const int max = 100 ;
-
- ////////////////////////////////////
-
- int main()
- {
- char buffer[max] ;
- ofstream file_out("OUTPUT.DAT" , ios::out | ios::app) ;
- if (!file_out)
- {
- cout << "OPEN FAILED\n" ;
- PAUSE() ;
- exit(1) ;
- }
-
- cout << "Enter a line of data: " ;
- while (!cin.get(buffer , max).eof\())
- {
- file_out << buffer << endl ;
- cout << "Next line: " ;
- cin >> FLUSH ;
- }
-
- return 0 ;
- }
-
- Finally, this program numbers and prints the records that were just written.
-
- // EXAMPLE FILEIO-03
-
- // NOW READ THE FILE THAT WAS JUST
- // CREATED
-
- #include <header.h>
-
- const int max = 100 ;
-
- ////////////////////////////////////
-
- int main()
- {
- char buffer[max] ;
- ifstream file_in("OUTPUT.DAT") ;
- if (!file_in)
- {
- cout << "OPEN FAILED\n" ;
- PAUSE() ;
- exit(1) ;
- }
-
- int rec = 0 ;
- while (!file_in.get(buffer , max).eof())
- {
- cout << "Record #"
- << ++rec
- << ": "
- << buffer
- << endl ;
- file_in >> FLUSH ;
- }
-
- return 0 ;
- }
-
- ****************************************************************
- The File Position Markers
-
- So that the file I/O classes can keep track of where in a file the data is
- to be written to and read from, they establish what is called a 'file position
- marker' (fpm). On Turbo C++ this marker has been 'typedef'ed as a 'long'
- integer representing an offset value from the beginning of the file. In point
- of fact, there are two such markers, one for reading, and one for writing.
- You may alter these markers by using two member functions: "seekg" and
- "seekp". "seekg" is associated with the file's 'get or read' pointer, and
- "seekp" with the file's 'put or write' pointer. The declarations for these two
- functions are as follows:
-
- istream& istream::seekg(streampos offset) ;
- istream& istream::seekg(streamoff offset , seek_dir) ;
-
- ostream& ostream::seekp(streampos offset) ;
- ostream& ostream::seekp(streamoff offset , seek_dir) ;
-
- where "streampos" and "streamoff" represent "long" integers, and "seek_dir"
- is an enumerated type defined as follows:
-
- enum seek_dir {ios::beg , ios::cur , ios::end} ;
-
- If the 1-argument form of the function is used, then "offset" is the offset
- from the beginning of the file. If the 2-argument form is used, then "offset"
- is the offset number of bytes (positive or negative) from the absolute
- "seek_dir" position. Therefore, a call to "seekg" with an argument of 0 causes
- the file to rewind and data to be read starting with the first record.
-
- To find out the positions of these markers, you may use the member functions
- "tellg" and "tellp". They are declared as follows:
-
- streampos istream::tellg() ;
- streampos ostream::tellp() ;
-
- To illustrate this, here is the previous example of writing and reading a
- file, but now it uses an instance of the class "fstream" so that the output and
- input can be combined. After each record is read and printed, the file
- position marker (fpm) is displayed. NOTE: The inclusion of the mode
- "ios::binary" is to avoid a Borland bug with "tellg" corrupting the fpm.
-
- // EXAMPLE FILEIO-04
-
- // HOW TO WRITE AND READ A FILE IN
- // THE SAME PROGRAM
-
- // NOTE: FILE IS OPENED IN BINARY
- // MODE TO AVOID A tellg BUG IN
- // TURBO C++
-
- #include <header.h>
-
- const int max = 100 ;
-
- ////////////////////////////////////
-
- int main()
- {
- char buffer[max] ;
- "fstream"file_both("BOTH.DAT" ,
- ios::in |
- ios::out |
- ios::binary) ;
- if (!file_both)
- {
- cout << "OPEN FAILED\n" ;
- PAUSE() ;
- exit(1) ;
- }
-
- cout << "Enter a line of data: " ;
- while (!cin.get(buffer , max).eof())
- {
- file_both << buffer << endl ;
- cout << "Next line: " ;
- cin >> FLUSH ;
- }
-
- // Flush the output buffer
- file_both << flush ;
-
- // Return to the start of the file
- file_both.seekg(0) ;
-
- // Read and print the records, and
- // show the file position marker
- int rec = 0 ;
- while (!file_both.get(buffer , max).eof())
- {
- cout << "Record #"
- << ++rec
- << ": "
- << buffer
- << endl ;
- cout << "fpm is: "
- << file_both.tellg()
- << endl ;
- file_both >> FLUSH ;
- }
-
- return 0 ;
- }
-
- This program gives the user complete flexibility as to the name of the
- external file to be manipulated, and modes to be used. NOTE: Do not try to
- include an instance of a file class within a class declaration. As of version
- 1.01, there is a bug which does not allow this. As an avoidance technique,
- you may include a pointer of the class type, and then obtain the space for
- the instance via the "new" operator.
-
- // EXAMPLE FILEIO-05
-
- // GIVE THE USER COMPLETE FLEXIBILITY
- // AS TO THE MODE OF THE FILE AND THE
- // DATA WRITTEN AND READ
-
- // ARGUMENTS ARE ENTERED FROM THE DOS
- // COMMAND LINE. THE FIRST ARGUMENT
- // IS THE DISK FILE NAME, AND THE
- // REMAINING ARGUMENTS REPRESENT THE
- // VARIOUS OPEN MODES, EXACTLY AS
- // SPECIFIED BY THE ENUMERATED TYPES
-
- // NOTE THAT A CLASS IS NOW USED TO
- // CONTROL THE VARIOUS OPERATIONS ON
- // A FILE OBJECT. HOWEVER, A POINTER
- // IS DECLARED INSTEAD OF AN INSTANCE
- // TO AVOID A BORLAND BUG
-
- #include <header.h>
-
- const int max = 100 ;
-
- class file
- {
- fstream* ptr ;
- public:
-
- file()
- {
- ptr = new fstream() ;
- }
-
- int open(int argc , char* argv[]) ;
- void read() ;
- void write() ;
- void beginning() ;
- void end() ;
- void close() ;
- } ;
-
- // Open the file by setting up the
- // field 'mode' with the OR of what-
- // ever modes the user has chosen
-
- int file::open(int argc , char* argv[])
- {
- int mode = 0 ;
-
- for (int i = 2 ; i < argc ; ++i)
- {
- if (!strcmp (argv[i] , "out"))
- mode |= ios::out ;
- else if (!strcmp (argv[i] , "in"))
- mode |= ios::in ;
- else if (!strcmp (argv[i] , "app"))
- mode |= ios::app ;
- else if (!strcmp (argv[i] , "ate"))
- mode |= ios::ate ;
- else if (!strcmp (argv[i] , "trunc"))
- mode |= ios::trunc ;
- else if (!strcmp (argv[i] , "nocreate"))
- mode |= ios::nocreate ;
- else if (!strcmp (argv[i] , "noreplace"))
- mode |= ios::noreplace ;
- else if (!strcmp (argv[i] , "binary"))
- mode |= ios::binary ;
- else
- cout << "Invalid mode: "
- << argv[i]
- << endl ;
- }
-
- // Perform the actual open
- ptr->open(argv[1] , mode) ;
-
- // If an error occurred, return
- // "true"
- return (!*ptr) ;
- }
-
- // Read the file
-
- void file::read()
- {
- char buffer [max] ;
-
- cout << "Data line: " ;
- ptr->get(buffer , max) ;
- *ptr >> FLUSH ;
- if (!(ptr->eof()))
- cout << buffer << endl ;
- else
- cout << "EOF\n" ;
- ptr->clear() ;
- }
-
- // Write the file
-
- void file::write()
- {
- char buffer [max] ;
-
- cout << "Enter some data: " ;
- cin.get(buffer , max) ;
- cin >> FLUSH ;
- *ptr << buffer << endl ;
- }
-
- // Return to the start of the file
-
- void file::beginning()
- {
- ptr->seekg(0) ;
- ptr->seekp(0) ;
- }
-
- // Seek to the end of the file
-
- void file::end()
- {
- ptr->seekg(0 , ios::end) ;
- ptr->seekp(0 , ios::end) ;
- }
-
- // Close the file
-
- void file::close()
- {
- ptr->close() ;
- }
-
- ///////////////////////////////////
-
- char menu() ;
-
- int main(int argc , char* argv [])
- {
- file my_file ;
-
- if (argc < 2)
- {
- cout << "NO FILE NAME\n" ;
- exit(1) ;
- }
-
- if(my_file.open(argc , argv))
- {
- cout << "OPEN FAILED\n" ;
- exit(2) ;
- }
-
- char ch ;
- while ((ch = menu()) != 'X')
- {
- switch (ch)
- {
- case 'R' : my_file.read() ;
- break ;
- case 'W' : my_file.write() ;
- break ;
- case 'B' : my_file.beginning() ;
- break ;
- case 'E' : my_file.end() ;
- break ;
- default : cout << "INVALID\n" ;
- break ;
- }
- }
- my_file.close() ;
-
- return 0 ;
- }
-
- ///////////////////////////////////////
-
- char menu()
- {
- cout << "\t(R)ead a record\n" ;
- cout << "\t(W)rite a record\n" ;
- cout << "\t(B)eginning of file\n" ;
- cout << "\t(E)nd of the file\n" ;
- cout << "\te(X)it\n" ;
-
- cout << "\n\tYour choice: " ;
- char ch ;
- cin >> ch >> FLUSH ;
- return (toupper(ch)) ;
- }
-
- ****************************************************************
- Using the Line Printer
-
- The line printer is just another output file insofar as DOS is concerned. To
- redirect output to a printer from within your program, use the predefined
- name "prn".
-
- // EXAMPLE FILEIO-06
-
- // HOW TO PRINT FROM WITHIN A
- // PROGRAM
-
- #include <header.h>
-
- int main()
- {
- ofstream printer ;
- // "prn" is the DOS printer
- printer.open("prn") ;
- if (!printer)
- {
- cerr << "Can't open\n" ;
- exit(1) ;
- }
- printer << "This line appears"
- << " on the printer\n" ;
-
- return 0 ;
- }
-
-